package org.simpleframework.core;

import lombok.extern.slf4j.Slf4j;
import org.simpleframework.aop.annotation.Aspect;
import org.simpleframework.core.annotation.Component;
import org.simpleframework.core.annotation.Controller;
import org.simpleframework.core.annotation.Repository;
import org.simpleframework.core.annotation.Service;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 容器类
 */
public class BeanContainer {
    /**
     * 存放所有被配置标记的目标对象的Map
     */
    private final ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<>();
    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATIONS
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);

    /**
     * 枚举懒汉单例模式 实现 BeanContainer的获取
     */
    public static BeanContainer  getInstance() {
        return ContainerHolder.HOLDER.instance;
    }
    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;
        // 懒汉模式:
        ContainerHolder() {
            instance = new BeanContainer();
        }
    }

    // 加载标记: 一个容器只会被加载一次
    private boolean loaded = false;
    public boolean isLoaded() { return loaded; }

    /**
     * 加载指定范围下面的被注解标记的目标Bean
     *
     * 因为只能加载一次,所以直接用synchronized实现比较方便,当然也能用双重验证锁
     * @param packageName
     */
    public synchronized void loadBeans(String packageName) {
        // 1) 如果加载过了, 则直接return
        if(loaded) return;
        // 2) 通过ClassUtil.extractPackageClasses()获得指定包下所有类对象
        Set<Class<?>> classes = ClassUtil.extractPackageClasses(packageName);
        // 3) 遍历set, 对每一个Class判断其是否被 我们创建的注解修饰, 如果是, 则要注册到容器中
        for (Class<?> clazz : classes) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATIONS) {
                if(clazz.isAnnotationPresent(annotation)) {
                    beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
                }
            }
        }
        // 4) 加载完毕,设置为true
        loaded = true;
    }

    /**
     * 添加一个class对象及其Bean实例
     *
     * @param clazz Class对象
     * @param bean  Bean实例
     * @return 原有的Bean实例, 没有则返回null
     */
    public Object addBean(Class<?> clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    /**
     * 移除一个IOC容器管理的对象
     *
     * @param clazz Class对象
     * @return 删除的Bean实例, 没有则返回null
     */
    public Object removeBean(Class<?> clazz) {
        return beanMap.remove(clazz);
    }

    /**
     * 根据Class对象获取Bean实例
     *
     * @param clazz Class对象
     * @return Bean实例
     */
    public Object getBean(Class<?> clazz) {
        return beanMap.get(clazz);
    }

    /**
     * 获取容器管理的所有Class对象集合
     *
     * @return Class集合
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }

    /**
     * 获取所有Bean集合
     *
     * @return Bean集合
     */
    public Set<Object> getBeans(){
        return new HashSet<>( beanMap.values());
    }

    /**
     * 根据注解筛选出Bean的Class集合
     *
     * @param annotation 注解
     * @return Class集合
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            return null;
        }
        //2.通过注解筛选被注解标记的class对象，并添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //类是否有相关的注解标记
            if(clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }
    /**
     * 通过接口或者父类获取实现类或者子类的Class集合，不包括其本身
     *
     * @param interfaceOrClass 接口Class或者父类Class
     * @return Class集合
     */
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            return null;
        }
        //2.判断keySet里的元素是否是传入的接口或者类的子类，如果是，就将其添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for(Class<?> clazz : keySet){
            //判断keySet里的元素是否是传入的接口或者类的子类
            if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0 ? classSet: null;
    }

    /**
     * 获得注册Bean个数
     * @return
     */
    public int size() {
        return beanMap.size();
    }
}
